<?php

declare(strict_types=1);

namespace app\service\notify;

use app\model\NotifyTemplate;
use InvalidArgumentException;
use think\Paginator;

class TemplateService
{
    private const STATUS_LABELS = [
        NotifyTemplate::STATUS_DISABLED => '禁用',
        NotifyTemplate::STATUS_ENABLED => '启用',
    ];

    public function paginate(array $filters, int $page, int $pageSize): Paginator
    {
        $query = NotifyTemplate::order('id', 'desc');

        if (!empty($filters['keyword'])) {
            $keyword = trim((string) $filters['keyword']);
            $query->where(static function ($subQuery) use ($keyword) {
                $subQuery->whereLike('name', '%' . $keyword . '%')
                    ->whereOr('title', 'like', '%' . $keyword . '%')
                    ->whereOr('type', 'like', '%' . $keyword . '%');
            });
        }

        if (($filters['status'] ?? '') !== '') {
            $query->where('status', (int) $filters['status']);
        }

        foreach (['category', 'type'] as $field) {
            if (($filters[$field] ?? '') !== '') {
                $query->where($field, trim((string) $filters[$field]));
            }
        }

        return $query->paginate([
            'list_rows' => $pageSize,
            'page' => $page,
        ]);
    }

    public function findById(int $id): ?NotifyTemplate
    {
        return NotifyTemplate::find($id);
    }

    public function create(array $payload): NotifyTemplate
    {
        $data = $this->extractPayload($payload, true);
        $this->ensureTypeUnique($data['type']);

        /** @var NotifyTemplate $template */
        $template = NotifyTemplate::create($data);

        return $this->findById((int) $template->id);
    }

    public function update(NotifyTemplate $template, array $payload): NotifyTemplate
    {
        $data = $this->extractPayload($payload, false, $template);
        if ($data === []) {
            return $this->findById((int) $template->id) ?? $template;
        }

        if (array_key_exists('type', $data) && $data['type'] !== (string) $template->type) {
            $this->ensureTypeUnique($data['type'], (int) $template->id);
        }

        $template->save($data);

        return $this->findById((int) $template->id);
    }

    public function delete(NotifyTemplate $template): bool
    {
        return (bool) $template->delete();
    }

    public function format(NotifyTemplate $template): array
    {
        $channels = $template->channels;
        $channels = is_array($channels) ? $channels : ($channels ? (array) json_decode((string) $channels, true) : []);
        if (!is_array($channels)) {
            $channels = [];
        }

        $status = (int) $template->status;

        return [
            'id' => (int) $template->id,
            'name' => $template->name,
            'title' => $template->title,
            'content' => $template->content,
            'type' => $template->type,
            'category' => $template->category,
            'channels' => $channels,
            'status' => $status,
            'status_label' => self::STATUS_LABELS[$status] ?? '未知',
            'create_time' => $template->create_time,
            'update_time' => $template->update_time,
        ];
    }

    private function extractPayload(array $payload, bool $isCreate, ?NotifyTemplate $template = null): array
    {
        $data = [];

        if ($isCreate || array_key_exists('name', $payload)) {
            $name = trim((string) ($payload['name'] ?? ''));
            if ($name === '') {
                throw new InvalidArgumentException('模板名称不能为空');
            }
            $data['name'] = $name;
        }

        if ($isCreate || array_key_exists('title', $payload)) {
            $title = trim((string) ($payload['title'] ?? ''));
            if ($title === '') {
                throw new InvalidArgumentException('模板标题不能为空');
            }
            $data['title'] = $title;
        }

        if ($isCreate || array_key_exists('content', $payload)) {
            $content = (string) ($payload['content'] ?? '');
            if ($content === '') {
                throw new InvalidArgumentException('模板内容不能为空');
            }
            $data['content'] = $content;
        }

        if ($isCreate || array_key_exists('type', $payload)) {
            $type = trim((string) ($payload['type'] ?? ''));
            if ($type === '') {
                throw new InvalidArgumentException('模板类型不能为空');
            }
            $data['type'] = $type;
        }

        if ($isCreate || array_key_exists('category', $payload)) {
            $category = trim((string) ($payload['category'] ?? ''));
            $data['category'] = $category === '' ? 'system' : $category;
        }

        if ($isCreate || array_key_exists('channels', $payload)) {
            $data['channels'] = $this->normaliseChannels($payload['channels'] ?? null);
        }

        if ($isCreate || array_key_exists('status', $payload)) {
            $status = (int) ($payload['status'] ?? NotifyTemplate::STATUS_ENABLED);
            if (!in_array($status, [NotifyTemplate::STATUS_DISABLED, NotifyTemplate::STATUS_ENABLED], true)) {
                throw new InvalidArgumentException('状态值不正确');
            }
            $data['status'] = $status;
        }

        return $data;
    }

    private function normaliseChannels($channels): ?array
    {
        if ($channels === null || $channels === '') {
            return null;
        }

        if (is_string($channels)) {
            $decoded = json_decode($channels, true);
            if (json_last_error() !== JSON_ERROR_NONE) {
                throw new InvalidArgumentException('渠道配置必须是有效的JSON');
            }
            $channels = $decoded;
        }

        if (!is_array($channels)) {
            throw new InvalidArgumentException('渠道配置格式必须为数组');
        }

        return $channels;
    }

    private function ensureTypeUnique(string $type, ?int $excludeId = null): void
    {
        $query = NotifyTemplate::where('type', $type);
        if ($excludeId !== null) {
            $query->where('id', '<>', $excludeId);
        }

        if ($query->find()) {
            throw new InvalidArgumentException('模板类型已存在');
        }
    }
}
